package net.flowas.genericcrud.rest;

import static net.flowas.genericcrud.service.ShareBoardConstant.BASE64_PREFIX;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.DigestInputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Base64;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.function.Function;

import javax.json.JsonObject;
import javax.ws.rs.Consumes;
import javax.ws.rs.GET;
import javax.ws.rs.POST;
import javax.ws.rs.PUT;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.core.MediaType;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import net.flowas.genericcrud.RepositoryService;
import net.flowas.genericcrud.dto.QueryOption;
import net.flowas.genericcrud.model.FileInfo;
import net.flowas.genericcrud.service.ShareBoardConstant;
/**
 * 文件操作：上传、下载
 * @author liujh
 *
 */
public class FileResource {
  private static final Logger LOG = LoggerFactory.getLogger(FileResource.class);
  private static final String MISSING_IMAGE = "META-INF/resources/missing/notExists.jpg";
  private RepositoryService viewHandler;
  private String fileStoragePath;
  private Function<FileInfo, Map> thumbnailGenerator;

  public void setThumbnailGenerator(Function<FileInfo, Map> thumbnailGenerator) {
    this.thumbnailGenerator = thumbnailGenerator;
  }

  @PUT
  @Path("/normal/base64Upload")
  @Consumes(MediaType.APPLICATION_JSON)
  public FileInfo base64Upload(JsonObject entity) {
    String originalFileName = entity.getString("fileName");
    String base64 = entity.getString("base64data");
    String uniqFileName = UUID.randomUUID().toString();
    String fileExt = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
    //if ("form-data".equals(fileExt)) {
    //  fileExt = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
    //}
    java.nio.file.Path path = Paths.get(fileStoragePath, uniqFileName);
    try {
      // ...
      //String base64 = base64data.toString();//new String(image,"UTF-8");
      base64 = base64.substring(base64.indexOf(BASE64_PREFIX) + BASE64_PREFIX.length());
      byte[] bytesData = Base64.getDecoder().decode(base64);
      Files.write(path, bytesData);
      return saveFileToLocal(originalFileName, fileExt, path.toFile());
    } catch (IOException e) {
      LOG.error("上传文件时发生异常[" + originalFileName + "]", e);
      throw new IllegalArgumentException(e.getMessage());
    }
  }
  
  /**
   * base64编码文件内容上传.
   * <p>要求数据头部包含contentType和编码信息，如: “data:image/gif;base64,”</p>
   * @param image 文件内容
   * @param originalFileName 原始文件名
   * @return
   */
  @POST
  @Path("/normal/binaryUpload")
  @Consumes(MediaType.WILDCARD)
  public FileInfo binaryUpload(byte[] image, @QueryParam("fileName") String originalFileName) {
    String uniqFileName = UUID.randomUUID().toString();
    String fileExt = originalFileName.substring(originalFileName.lastIndexOf('.') + 1);
    java.nio.file.Path path = Paths.get(fileStoragePath, uniqFileName);
    try {
      // ...
      String base64 = new String(image,"UTF-8");
      base64 = base64.substring(base64.indexOf(BASE64_PREFIX) + BASE64_PREFIX.length());
      byte[] bytesData = Base64.getDecoder().decode(base64);
      Files.write(path, bytesData);
      return saveFileToLocal(originalFileName, fileExt, path.toFile());
    } catch (IOException e) {
      LOG.error("上传文件时发生异常[" + originalFileName + "]", e);
      throw new IllegalArgumentException(e.getMessage());
    }
  }

  private FileInfo saveFileToLocal(String originalFileName, String fileExt, File tmpFile)
      throws IOException {
    String checkCode = generateChechCode(tmpFile);
    java.nio.file.Path targetFile = Paths.get(fileStoragePath, checkCode + "." + fileExt);
    if (targetFile.toFile().exists()) {
      if(!tmpFile.delete()) {
      	tmpFile.deleteOnExit();
      }  
      Map<String, Object> conditions = new HashMap<>();
      conditions.put("filePath", checkCode + "." + fileExt);
      QueryOption<Map<String, Object>> option = new QueryOption<>(conditions,1,1);     
	  List<FileInfo> exists = viewHandler.findRang(option , FileInfo.class);
      if (exists.isEmpty()) {
        String msg = "相同的文件已经上传了,但是数据库中没有记录,已删除错误文件！";
        LOG.error(msg+targetFile.getFileName());
        Files.delete(targetFile);        
        throw new IllegalArgumentException(msg);
      } else {    	
        return exists.iterator().next();
      }
    } else {
      Files.move(tmpFile.toPath(), targetFile);
      FileInfo fileInfo = new FileInfo();
      fileInfo.setFilePath(targetFile.getFileName().toString());
      fileInfo.setOriginalFileName(originalFileName);
      thumbnailGenerator.apply(fileInfo);
      // 触发查询监听，以便转化地址信息
      fileInfo = (FileInfo) viewHandler.saveOrUpdate(fileInfo);
      viewHandler.detach(fileInfo);
      fileInfo = viewHandler.findById(FileInfo.class, fileInfo.getId());
      return fileInfo;
    }
  }

  /**
   * 下载文件.
   * 
   * @param fileName
   *          文件标识
   * @return 文件句柄
   */
  @GET
  @Path("/normal/download/{fileName}")
  @Produces(MediaType.APPLICATION_OCTET_STREAM)
  public InputStream download(@PathParam("fileName") String fileName,
      @QueryParam("selector") String selector) {
    if ("thumbnail".equals(selector)) {
      int index = fileName.lastIndexOf('.');
      String ext = fileName.substring(index);
      fileName = fileName.substring(0, index);
      fileName = fileName + "/thambnail" + ext;
    }
    java.nio.file.Path path = Paths.get(fileStoragePath, fileName);
    File file = path.toFile();    
    try {
		return new FileInputStream(file);
	} catch (FileNotFoundException e) {
		LOG.error("Error occured when downloading!",e);
		URL url = FileResource.class.getClassLoader().getResource(MISSING_IMAGE);
	    try {
			return url.openStream();
		} catch (IOException e1) {
			LOG.error("can not open file :"+MISSING_IMAGE,e1);
			return null;
		}
	}    
  }

  /**
   * 图片下载,返回图片的base64编码内容.
   * 
   * @param request
   *          请求参数
   * @return 图片文件列表(base64编码)
   */
  @POST
  @Path("/normal/downloadImages")
  @Consumes(MediaType.APPLICATION_JSON)
  @Produces(MediaType.APPLICATION_JSON)
  public Map downloadImages(Map request) {
    List<String> neededImages = (List<String>) request.get("IMAGE_NAMES");
    HashMap result = new HashMap();
    List list = new ArrayList();
    for (String imageName : neededImages) {
      HashMap imageContext = new HashMap();
      imageContext.put("IMAGE_NAME", imageName);
      String base64 = readFileContext(fileStoragePath, imageName);
      //imageContext.put("BASE64_CONTENT", base64);
      imageContext.put("base64data", base64);
      list.add(imageContext);
    }
    result.put("IMAGE_CONTENT", list);
    return result;
  }

  private String readFileContext(String dir, String name) {
    String base64;
    try {
      base64 = Base64.getEncoder().encodeToString(Files.readAllBytes(Paths.get(dir, name)));
      return base64;
    } catch (IOException e) {
      LOG.error("读取和编码文件内容时发生异常[" + name + "]", e);
    }
    return null;
  }

  public void setFileStoragePath(String fileStoragePath) {
    this.fileStoragePath = fileStoragePath;
  }

  public void setViewHandler(RepositoryService viewHandler) {
    this.viewHandler = viewHandler;
  }

  private String generateChechCode(File file) {
    MessageDigest digest = null;
    try (FileInputStream inputStram = new FileInputStream(file)) {
      digest = MessageDigest.getInstance(ShareBoardConstant.ALGORITHM);
      DigestInputStream din = new DigestInputStream(inputStram, digest);
      while (din.read() != -1) {
      }
      din.close();
    } catch (NoSuchAlgorithmException | IOException exception) {
      throw new IllegalStateException(exception);
    }
    byte[] bytes = digest.digest();
    return Base64.getUrlEncoder().encodeToString(bytes);
  }
}
